//@version=5
indicator("Extreme Candle Pattern Visualizer", overlay=true, max_lines_count=500, max_labels_count=500)

// Input settings
lookbackPeriod = input.int(2000, "Lookback Period", minval=10, maxval=5000, tooltip="How many bars back to scan (lower = faster)")
showLabels = input.bool(true, "Show % Labels", tooltip="Display percentage labels on highlighted candles")
minPctThreshold = input.float(0.5, "Min % Threshold", minval=0.01, tooltip="Minimum % change to consider (filters noise)")
showPercentile = input.bool(true, "Show Percentile", tooltip="Display current candle's percentile ranking")

// Store the latest candle's data (reference candle)
var float latestCandlePct = 0.0
var bool latestIsPositive = false
var bool latestIsNegative = false

// Update reference candle data when on the last bar
if barstate.islast
    latestCandlePct := ((close - open) / open) * 100
    latestIsPositive := latestCandlePct > 0
    latestIsNegative := latestCandlePct < 0

// Function to calculate candle percentage change
calcCandlePct(o, c) =>
    ((c - o) / o) * 100

// Arrays to store highlighted candle data
var line[] recoveryLines = array.new_line(0)
var label[] pctLabels = array.new_label(0)
var label[] highlightedMarks = array.new_label(0)
var label[] percentileLabel = array.new_label(0)

// Clear old drawings on new bar
if barstate.islast
    if array.size(recoveryLines) > 0
        for i = array.size(recoveryLines) - 1 to 0
            line.delete(array.get(recoveryLines, i))
        array.clear(recoveryLines)
    
    if array.size(pctLabels) > 0
        for i = array.size(pctLabels) - 1 to 0
            label.delete(array.get(pctLabels, i))
        array.clear(pctLabels)
    
    if array.size(highlightedMarks) > 0
        for i = array.size(highlightedMarks) - 1 to 0
            label.delete(array.get(highlightedMarks, i))
        array.clear(highlightedMarks)
    
    if array.size(percentileLabel) > 0
        for i = array.size(percentileLabel) - 1 to 0
            label.delete(array.get(percentileLabel, i))
        array.clear(percentileLabel)

// Main logic - only execute on the last bar to analyze history
if barstate.islast
    // Array to store all percentage changes for percentile calculation
    var float[] allPctChanges = array.new_float(0)
    array.clear(allPctChanges)
    
    // Collect all candle % changes in lookback period
    for i = 1 to math.min(lookbackPeriod, bar_index)
        histOpen = open[i]
        histClose = close[i]
        histPct = calcCandlePct(histOpen, histClose)
        array.push(allPctChanges, histPct)
    
    // Add current candle to the array
    array.push(allPctChanges, latestCandlePct)
    
    // Sort array to calculate percentile
    array.sort(allPctChanges, order.ascending)
    
    // Find position of current candle in sorted array
    totalBars = array.size(allPctChanges)
    currentPosition = 0
    for i = 0 to totalBars - 1
        if array.get(allPctChanges, i) <= latestCandlePct
            currentPosition := i + 1
    
    // Calculate percentile (0-100)
    percentile = (currentPosition / totalBars) * 100
    
    // Display percentile label
    if showPercentile
        labelText = "Current: " + str.tostring(math.round(latestCandlePct, 2)) + "%\n" + 
                     "Percentile: " + str.tostring(math.round(percentile, 1)) + "%"
        
        // Color based on extremity
        labelColor = color.gray
        if percentile <= 10
            labelColor := color.new(color.red, 20)
        else if percentile >= 90
            labelColor := color.new(color.green, 20)
        else if percentile <= 25
            labelColor := color.new(color.orange, 20)
        else if percentile >= 75
            labelColor := color.new(color.lime, 20)
        
        percLabel = label.new(bar_index, high, labelText, 
                             color=labelColor, 
                             textcolor=color.white, 
                             style=label.style_label_down, 
                             size=size.normal,
                             yloc=yloc.abovebar)
        array.push(percentileLabel, percLabel)
    
    // Loop through historical bars for recovery tracking
    for i = 1 to math.min(lookbackPeriod, bar_index)
        histOpen = open[i]
        histHigh = high[i]
        histLow = low[i]
        histClose = close[i]
        histPct = calcCandlePct(histOpen, histClose)
        histBarIndex = bar_index - i
        
        shouldHighlight = false
        
        // Check if historical candle meets criteria (compare against latest candle)
        if latestIsNegative and histPct <= latestCandlePct and math.abs(histPct) >= minPctThreshold
            shouldHighlight := true
        else if latestIsPositive and histPct >= latestCandlePct and math.abs(histPct) >= minPctThreshold
            shouldHighlight := true
        
        // If candle should be highlighted, track recovery
        if shouldHighlight
            isFalling = histClose < histOpen
            isRising = histClose > histOpen
            
            // Track price movement after this candle until recovery
            extremePrice = histClose
            extremeBarIndex = histBarIndex
            recovered = false
            
            // Look forward from this historical candle to find extreme and recovery
            for j = i - 1 to 0
                futureHigh = high[j]
                futureLow = low[j]
                futureClose = close[j]
                futureBarIndex = bar_index - j
                
                if not recovered
                    if isFalling
                        // Track lowest point before recovering back to start price
                        if futureLow < extremePrice
                            extremePrice := futureLow
                            extremeBarIndex := futureBarIndex
                        // Check if recovered back to starting price
                        if futureHigh >= histOpen
                            recovered := true
                    else if isRising
                        // Track highest point before recovering back to start price
                        if futureHigh > extremePrice
                            extremePrice := futureHigh
                            extremeBarIndex := futureBarIndex
                        // Check if recovered back to starting price
                        if futureLow <= histOpen
                            recovered := true
            
            // Draw line from candle close to extreme point
            if extremePrice != histClose
                lineColor = isFalling ? color.new(color.red, 0) : color.new(color.green, 0)
                newLine = line.new(histBarIndex, histClose, extremeBarIndex, extremePrice, color=lineColor, width=2)
                array.push(recoveryLines, newLine)
                
                // Calculate percentage move
                pctMove = ((extremePrice - histClose) / histClose) * 100
                
                // Add label if enabled
                if showLabels
                    labelText = str.tostring(math.round(pctMove, 2)) + "%"
                    labelY = isFalling ? extremePrice : extremePrice
                    labelStyle = isFalling ? label.style_label_down : label.style_label_up
                    newLabel = label.new(extremeBarIndex, labelY, labelText, color=lineColor, textcolor=color.white, style=labelStyle, size=size.small)
                    array.push(pctLabels, newLabel)
        
            
            // Position mark on top of falling bars, bottom of rising bars
            markY = isFalling ? histHigh : histLow
            markStyle = isFalling ? label.style_circle : label.style_circle
            
            highlightMark = label.new(histBarIndex, markY, "", color=color.new(#fda727, 0), textcolor=color.new(color.yellow, 0), style=markStyle, size=size.tiny)
            array.push(highlightedMarks, highlightMark)

// Plot hollow candles for all bars (historical bars will be highlighted with yellow marks)
hollowColor = color.new(color.white, 100)  // Fully transparent = hollow
barcolor(hollowColor)

// Plot reference line showing current candle's close price for context
plot(close, "Reference Close", color=color.new(color.blue, 70), linewidth=1, style=plot.style_circles)